package br.usp.poli.compilador2008.lexical;

public class LexAutomata {

	/**
	 * Class LexAutomata - is an implementation of an automata, has the digits, numbers and
	 * symbols stored locally in an array, and also a matrix which correlates both symbol
	 * and desired token value. 
	 * 
	 * It has a private state, and a word value (which is the concatenation of all the read
	 * characters). When the nxtValue method is called, a symbol is given to the automata, which
	 * consumes it, and changes it internal state.
	 * 
	 * If an empty space or line break is inserted, it understands the word is finished, 
	 * and calls the method to check if the word is a reserved word, as defined in the 
	 * array.
	 * 
	 */

	private String State;
	private String word;

	//digits, characters and reserved words are stored in a vector, for faster access and ready methods
	private String[] dig = {"0","1","2","3","4","5","6","7","8","9"};

	private String[] letra = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q",
			"r","s","t","u","v","x","y","w","z", "A", "B","C", "D", "E","F","G", "H","I", "J", "K","L",
			"M", "N","O", "P", "Q","R","S", "T","U", "V", "X","Y","Z"};

	private String[] symbol = {"=", "<",">","+","-","*","/","(",")","{","}",",",";","[","]"};

	private String[][] tokenDescription = {{"int", "INT"},{"if","IF"},{"while","WHILE"},{"+","SOMA"},{"-","SUBT"},{"*","MULT"},{"/","DIV"},{">", "MAIOR"},
			{"<","MENOR"},{"==","IGUAL"},{"!=","DIFER"},{">=","MAI_OU_IG"},{"<=", "MEN_OU_IG"},{"(","ABRE_PAR"},
			{")","FECHA_PAR"},{"{","ABRE_ESC"},{"}","FECHA_ESC"},{"=","ATRIB"}, {";","P_E_VIRG"}, {",","VIRG"}, {"[", "ABR_COLC"}, {"]", "FEC_COL"}};

	// Construtor: starts the execution of the automata, setting the state to "Start
	public LexAutomata(){
		reset();
	}

	// Restarts the execution of the automata
	public void reset(){
		this.State = "Start";
		this.word = new String();
	}	


	public void nxtValue(char character) {
		char[] tmp = new char[1];
		tmp[0] = character;
		String carac = new String(tmp);
		nxtValue(carac);
	}

	public void nxtValue(String carac) {	
		if(carac.compareTo(" ")!=0 && carac.compareTo("\t")!=0 && carac.compareTo("\n")!=0){
			String Type = "Unknown";

			// Check if is a number
			for(int i=0;i<dig.length&&Type.compareTo("Number")!=0;i++){
				if(carac.compareTo(dig[i])==0){
					Type = "Number";				
				}
			}

			if(Type.compareTo("Number")!=0){
				// Check if is a letter
				for(int i=0;i<letra.length&&Type.compareTo("Letter")!=0;i++){
					if(carac.compareTo(letra[i])==0){
						Type = "Letter";
					}
				}

				if(Type.compareTo("Letter")!=0){		
					// Check if is a symbol
					for(int i=0;i<symbol.length&&Type.compareTo("Symbol")!=0;i++){
						if(carac.compareTo(symbol[i])==0){
							Type = "Symbol";
						}
					}			
				}
			}

			// If no state was found, then an error state is set
			if(Type.compareTo("Unknown")==0){
				this.word = word.concat(carac);
				State = "ERROR_UNK_CHAR";
			}

			// Defines the type of the token, based on the consumed caracter
			else {
				if(Type=="Number"){			

					this.word = word.concat(carac);

					if(this.State=="Start"){
						this.State="NUM";
					}
					if(this.State=="SYMB"){
						this.State="ERROR_UNK_WORD";
					}
					else if(this.State=="VEC1"){
						this.State = "E_VEC1";
					}

				}

				else if(Type=="Letter"){

					this.word = word.concat(carac);

					if(this.State=="Start"){
						this.State = "ID";
					}
					else if(this.State=="NUM"){
						this.State = "ERROR_UNK_WORD";
					}

					else if(this.State=="SYMB"){
						this.State = "ERROR_UNK_WORD";
					}
					else if(this.State=="VEC1"){
						this.State = "ERROR_UNK_WORD";
					}}

				else if (Type=="Symbol"){

					this.word = word.concat(carac);

					if(this.State=="Start"){
						this.State = "SYMB";
					}
					else if(this.State=="ID"){
						if(carac.compareTo("[")==0){
							this.State = "VEC1";
						}else {
							this.State = "ERRO_UNK_WORD"; }
					}
					else if(this.State=="E_VEC1"){
						if(carac.compareTo("]")==0){
							this.State = "E_VEC";
						} else {
							this.State = "ERRO_UNK_WORD";
						}
					}
					else if(this.State=="VEC1"){
						if(carac.compareTo("]")==0){
							this.State = "VEC";
						} else {
							this.State = "ERRO_UNK_WORD";
						}
					}
					else if(this.State=="NUM"){
						this.State = "ERRO_UNK_WORD";
					}
				}
			}
		}

		// When a space or page break is received, checks if the word is reserved
		updateState();
	}

	// If the word is reserved, then refreshes the state
	private void updateState(){

		if(word.length()<=5){
			for(int i=0;i<tokenDescription.length; i++){
				if(word.compareTo(tokenDescription[i][0])==0){
					this.State=tokenDescription[i][1];
				}
			}
		}
		else {
			if(this.State.compareTo("ID")!=0 && this.State.compareTo("NUM")!=0){
				this.State = "ERRO_UNK_WORD";
			}
		}
		System.out.println(this.word +"  "+ this.State);
	}

	//Returns the automata state
	public String getState(){
		return this.State;
	}

	//Returns the word
	public String getWord(){
		return this.word;
	}
}
